package com.moonLight.weblog.jwt.filter;


import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.moonLight.weblog.jwt.exception.UsernameOrPasswordNullException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;
/**
 * JWT 认证过滤器，用于处理登录请求并验证用户名和密码
 *
 * AbstractAuthenticationProcessingFilter 是 Spring Security 框架中的一个抽象类，它用于处理基于请求的认证逻辑。这个类的主要作用是提供了一个框架，用于在特定类型的请求到达时执行认证过程。
 * 以下是它的主要使用场景：
 * 特定请求触发认证：当应用程序需要根据特定类型的请求（如表单登录、HTTP Basic 认证或自定义认证机制）来触发认证逻辑时，可以继承 AbstractAuthenticationProcessingFilter 并实现具体的认证逻辑。
 * 认证过滤器链的一部分：通常，AbstractAuthenticationProcessingFilter 会被添加到 Spring Security 的过滤器链中，在请求到达目标资源之前进行认证检查。
 * 自定义认证机制：如果你需要实现自定义的认证机制（例如 JWT 认证），你可以继承 AbstractAuthenticationProcessingFilter 并重写其方法以适应你的需求。
 * 具体到你提到的 JwtAuthenticationFilter，它继承了 AbstractAuthenticationProcessingFilter，意味着它是一个专门用于处理 JWT 认证的过滤器。
 * 在这个上下文中，JwtAuthenticationFilter 会在每次 HTTP 请求到达时检查是否存在有效的 JWT，并根据该令牌进行用户身份验证。
 * 总结
 * AbstractAuthenticationProcessingFilter 主要用于构建自定义认证过滤器，特别是在需要根据特定类型的请求来触发认证逻辑的情况下。对于 JWT 认证，JwtAuthenticationFilter 继承并扩展了这个抽象类，以便在每次请求时检查和验证 JWT。
 */
public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    /**
     * 无参构造函数
     * 指定用户登录的访问地址
     */
    public JwtAuthenticationFilter() {
        //调用父类的构造函数
        /*
        * AntPathRequestMatcher 实例，用于匹配特定的 URL 和 HTTP 方法。
            "/login"：表示该过滤器只处理路径为 /login 的请求。
            "POST"：表示该过滤器只处理 HTTP POST 请求
          符合过滤条件的 则执行 attemptAuthentication方法
          当有符合条件的请求到达时，Spring Security 会调用该过滤器的 doFilter 方法.
          进而调用 attemptAuthentication 方法来进行用户认证。
        * */
        super(new AntPathRequestMatcher("/login", "POST"));
    }

    /**
     * 尝试认证用户 将符合过滤条件的请求 执行该方法 获取登录用户信息
     *
     * @param request  HTTP请求，包含用户登录信息
     * @param response HTTP响应
     * @return 认证对象，包含认证结果
     * @throws AuthenticationException 如果认证失败，抛出此异常
     * @throws IOException 如果读取请求输入流时发生错误，抛出此异常
     * @throws ServletException 如果过滤器遇到Servlet异常，抛出此异常
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException {
        //jackson 反编译 -> 拿到需要的数据
        ObjectMapper mapper = new ObjectMapper();
        // 解析提交的 JSON 数据
        //request.getInputStream() 是 Java Servlet API 中的一个方法，用于获取 HTTP 请求的原始输入流。
        // 它并不是将整个请求“变成”输入流，而是提供了对请求体（即请求中的数据部分）的直接访问。
        JsonNode jsonNode = mapper.readTree(request.getInputStream());
        JsonNode usernameNode = jsonNode.get("username");
        JsonNode passwordNode =  jsonNode.get("password");

        // 判断用户名、密码是否为空
        if (Objects.isNull(usernameNode) || Objects.isNull(passwordNode)
            || StringUtils.isBlank(usernameNode.textValue()) || StringUtils.isBlank(passwordNode.textValue())) {
            throw new UsernameOrPasswordNullException("用户名或密码不能为空");
        }

        String username = usernameNode.textValue();
        String password = passwordNode.textValue();

        // 将用户名、密码封装到 Token 中
        /*
        * UsernamePasswordAuthenticationToken：这是 Spring Security 提供的一个类，用于封装用户名和密码的认证信息。
            构造函数参数：
            username：用户提供的用户名。
            password：用户提供的密码。
         这个对象表示一个尚未经过认证的认证请求。它包含了用户提交的凭据（用户名和密码），但还没有经过验证。
        * */
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken
                = new UsernamePasswordAuthenticationToken(username, password);
        /**
         * getAuthenticationManager()：这是一个方法调用，返回当前应用程序中的 AuthenticationManager 实例。
         * AuthenticationManager 是 Spring Security 中的核心组件之一，负责处理认证逻辑。
             * authenticate(...)：这是 AuthenticationManager 的一个方法，用于执行实际的认证过程。
             *  它接收一个 Authentication 对象（在这个例子中是 UsernamePasswordAuthenticationToken），
             *  并返回一个完全认证后的 Authentication 对象，或者在认证失败时抛出 AuthenticationException。
             *  authenticate(...)： 执行认证
         *          根据认证结果需要进行处理 handler包下的
         *                  RestAuthenticationSuccessHandler 自定义认证成功处理器 则返回 Token 令牌，(JSON)
         *                  RestAuthenticationFailureHandler 自定义认证失败处理器 则返回对应的提示信息
         */
        return getAuthenticationManager().authenticate(usernamePasswordAuthenticationToken);
    }
}
